﻿using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Zhy.DigitaPlatform.Common;
using Zhy.DigitaPlatform.DataAccess;
using Zhy.DigitaPlatform.DeviceAccess;
using Zhy.DigitaPlatform.DeviceAccess.Base;
using Zhy.DigitaPlatform.Entities;
using Zhy.DigitaPlatform.IDataAccess;
using Zhy.DigitaPlatform.Models;

namespace Zhy.DigitaPlatform.ViewModels
{
    /// <summary>
    /// 主窗口业务类
    /// </summary>
    public class MainViewModel : ViewModelBase
    {
        #region 属性
        private int _viewBlur = 0;
        /// <summary>
        /// 设置弹窗时主窗口显示蒙板效果
        /// </summary>
        public int ViewBlur
        {
            get { return _viewBlur; }
            set { Set(ref _viewBlur, value); }
        }
        /// <summary>
        /// 用户信息
        /// </summary>
        public UserModel GlobalUserInfo { get; set; } = new UserModel();

        private object _viewContent;
        /// <summary>
        /// 要显示的内容页面
        /// </summary>
        public object ViewContent
        {
            get { return _viewContent; }
            set { Set(ref _viewContent, value); }
        }

        /// <summary>
        /// 菜单数据集合
        /// </summary>
        public List<MenuModel> Menus { get; set; }
        /// <summary>
        /// 内容切换按钮
        /// </summary>
        public RelayCommand<object> SwitchPageCommand { get; set; }

        /// <summary>
        /// 用气排行数据
        /// </summary>
        public List<RankingItemModel> RankingList { get; set; }
        /// <summary>
        /// 设备提醒数据集合
        /// </summary>
        public List<MonitorWarnningModel> WarnningList { get; set; }
        /// <summary>
        /// 内容区域的组件集合
        /// </summary>
        public List<DeviceItemModel> DeviceList { get; set; }
        /// <summary>
        /// 组态编辑按钮事件
        /// </summary>
        public RelayCommand ComponentsConfigCommand { get; set; }
        #endregion
        /// <summary>
        /// 数据库操作类
        /// </summary>
        ILocalDataAccess _localDataAccess;

        /// <summary>
        /// 初始化
        /// </summary>
        /// <param name="localDataAccess">注册数据库操作类</param>
        public MainViewModel(ILocalDataAccess localDataAccess)
        {
            _localDataAccess = localDataAccess; // 实例化数据库操作类

            if (!IsInDesignMode)
            {
                // 主窗口数据

                #region 初始化菜单数据
                Menus = new List<MenuModel>();
                Menus.Add(new MenuModel
                {
                    IsSelected = true,
                    MenuHeader = "监控",
                    MenuIcon = "\ue639",
                    TargetView = "MonitorPage"
                });
                Menus.Add(new MenuModel
                {
                    MenuHeader = "趋势",
                    MenuIcon = "\ue61a",
                    TargetView = "TrendPage"
                });
                Menus.Add(new MenuModel
                {
                    MenuHeader = "报警",
                    MenuIcon = "\ue60b",
                    TargetView = "AlarmPage"
                });
                Menus.Add(new MenuModel
                {
                    MenuHeader = "报表",
                    MenuIcon = "\ue703",
                    TargetView = "ReportPage"
                });
                Menus.Add(new MenuModel
                {
                    MenuHeader = "配置",
                    MenuIcon = "\ue60f",
                    TargetView = "SettingsPage"
                });
                #endregion

                // 内容页面切换
                ShowPage(Menus[0]);
                SwitchPageCommand = new RelayCommand<object>(ShowPage); // 内容切换按钮事件

                // Monitor
                Random random = new Random();
                #region 用气排行-数据
                string[] quality = new string[] { "车间-1", "车间-2", "车间-3", "车间-4",
                "车间-5" };
                RankingList = new List<RankingItemModel>();
                foreach (var q in quality)
                {
                    RankingList.Add(new RankingItemModel()
                    {
                        Header = q,
                        PlanValue = random.Next(100, 200),
                        FinishedValue = random.Next(10, 150),
                    });
                }
                #endregion

                #region 设备提醒-数据
                WarnningList = new List<MonitorWarnningModel>()
                {
                  new MonitorWarnningModel{Message= "朝夕PLT-01：保养到期",
                      DateTime=DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") },
                  new MonitorWarnningModel{Message= "朝夕PLT-01：故障",
                      DateTime=DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") },
                  new MonitorWarnningModel{Message= "朝夕PLT-01：保养到期",
                      DateTime=DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") },
                  new MonitorWarnningModel{Message= "朝夕PLT-01：保养到期",
                      DateTime=DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") },
                  new MonitorWarnningModel{Message= "朝夕PLT-01：保养到期",
                      DateTime=DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") },
                  new MonitorWarnningModel{Message= "朝夕PLT-01：保养到期",
                      DateTime=DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") },
                  new MonitorWarnningModel{Message= "朝夕PLT-01：保养到期",
                      DateTime=DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") }
                };
                #endregion
                ComponentsConfigCommand = new RelayCommand(ShowConfig);

                // 初始化组件信息
                this.ComponentsInit();

                // 开始获取设备数据
                this.Monitor();
            }
        }
        /// <summary>
        /// 用于通知接收数据线程应用被取消
        /// </summary>
        CancellationTokenSource cts = new CancellationTokenSource();
        /// <summary>
        /// 记录获取组件数据的线程
        /// </summary>
        List<Task> tasks = new List<Task>();
        /// <summary>
        /// 开始获取设备数据
        /// </summary>
        private void Monitor()
        {
            // 通过统一的接口获取通信对象（提供对应的属性）
            // 获取对象后，进行方法的调用 
            // 多设备请求：
            // 1、设备集合依次轮询(一个设备处理完后，关闭连接，再开下个连接--短连接)  
            // 2、每个设备单独监视（每个设备一个线程，一个连接打开的后，直到应用支出再关闭连接）
            //    场景：设备   分离，   从一个设备中转     不允许多个连接    ModbusRTU   串口总线
            //    需要解决传输对象共用的问题    --- 之后多线程同时处理   对象 上锁
            Communication communication = new Communication();
            foreach (var item in DeviceList) // 循环设备集合
            {
                // 如果没有配置相关数据，不执行监视 - 比如管道等组件不需要监控
                if (item.PropList.Count == 0 || item.VariableList.Count == 0) continue;

                // 根据配置信息，创建协议执行对象
                var result_eo = communication.GetExecuteObject(
                    item.PropList.Select(p =>
                    new DevicePropItemEntity { PropName = p.PropName, PropValue = p.PropValue }
                    ).ToList());
                if (!result_eo.Status)
                {   // 通讯失败
                    // 异常提示信息
                    item.IsWarning = true;
                    item.WarningMessage = result_eo.Message;
                    continue;
                }

                // 根据协议对象，创建循环监视线程
                var task = Task.Run(async () =>  // 单独对设备进行循环监听
                {
                    // 处理刷新频率
                    int delay = 500;
                    // 获取监控组件的刷新频率
                    var dv = item.PropList.FirstOrDefault(p => p.PropName == "RefreshRate");
                    if (dv != null)
                        int.TryParse(dv.PropValue, out delay);
                    #region 循环监听前准备好变量数据-将变量数据打包好
                    /// 将当前设备中的所有点位数据   提供到Read方法
                    /// 
                    //Type.GetType("System.UInt16");
                    List<VariableProperty> variables =
                    item.VariableList.Select(v => new VariableProperty
                    {
                        VarId = v.VarNum,
                        VarAddr = v.VarAddress,
                        ValueType = Type.GetType("System." + v.VarType)
                    }).ToList();

                    Result<List<CommAddress>> result_addr = result_eo.Data.GroupAddress(variables);
                    if (!result_addr.Status)
                    {
                        // 异常提示信息
                        item.IsWarning = true;
                        item.WarningMessage = result_addr.Message;
                        return;
                    }

                    #endregion
                    // 计算对象 - 用于字符串计算
                    DataTable dataTable = new DataTable();
                    // 循环逻辑-循环读取组件数据
                    while (!cts.IsCancellationRequested)
                    {
                        await Task.Delay(delay); // 等待刷新频率
                        /// 处理数据 

                        var result_value = result_eo.Data.Read(result_addr.Data);  // 目前：里面做打包   
                        if (!result_value.Status)
                        {
                            // 异常提示信息
                            item.IsWarning = true;
                            item.WarningMessage = result_value.Message;
                            continue;
                        }
                        // 解析数据
                        foreach (var ma in result_addr.Data)
                        {
                            foreach (var vv in ma.Variables)
                            {
                                var dataBytes = vv.ValueBytes;
                                var id = vv.VariableId;
                                var dVar = item.VariableList.FirstOrDefault(v => v.VarNum == id);// 设备的变量记录
                                                                                                 //vp.VarId
                                                                                                 //vp.ValueBytes

                                // 需要进一步处理
                                //dVar.Value = communication.ConvertType(dataBytes, Type.GetType("System." + dVar.VarType));
                                Result<object> result_data = communication.ConvertType(dataBytes, Type.GetType("System." + dVar.VarType));
                                if (!result_data.Status)
                                {
                                    item.IsWarning = true;
                                    item.WarningMessage = result_data.Message;
                                    continue;
                                }
                                try
                                {
                                    dVar.Value = result_data.Data;
                                    // 获取的设备数据是200（result_data.Data）
                                    // 偏移量(dVar.Offset)  -20
                                    // 系数(dVar.Modulus)   0.1  
                                    // "200 * 0.1 + -20"  
                                    // 0
                                    if (dVar.VarType != "Boolean")
                                    {
                                        // // "200 * 0.1 + -20"  
                                        string exp = result_data.Data.ToString() + "*" + dVar.Modulus + "+" + dVar.Offset;
                                        // 对字符串的公式进行计算
                                        var result = dataTable.Compute(exp, "");

                                        dVar.Value = result;
                                    }
                                    //v.Value = BitConverter.ToUInt16(dataBytes, 0);

                                    // 字节序
                                }
                                catch (Exception ex)
                                {
                                    item.IsWarning = true;
                                    item.WarningMessage = ex.Message;
                                }

                                // 目前：通信平台中返回相关数据的字节
                                // 后续：希望封装第三方库，返回数据
                            }
                        }
                        // 关闭报警状态
                        item.IsWarning = false;
                    }
                    result_eo.Data.Dispose(); // 释放通讯资源
                }, cts.Token);
                tasks.Add(task);
            }
        }

        /// <summary>
        /// 初始化组件数据
        /// </summary>
        private void ComponentsInit()
        {
            var ds = _localDataAccess.GetDevices().Select(d =>
            {
                return new DeviceItemModel()
                {
                    X = double.Parse(d.X),
                    Y = double.Parse(d.Y),
                    Z = int.Parse(d.Z),
                    Width = double.Parse(d.W),
                    Height = double.Parse(d.H),
                    DeviceType = d.DeviceTypeName,
                    DeviceNum = d.DeviceNum,
                    FlowDirection = int.Parse(d.FlowDirection),
                    Rotate = int.Parse(d.Rotate),
                    // 属性集合
                    PropList = new ObservableCollection<DevicePropModel>(
                        d.Props.Select(dp => new DevicePropModel
                        {
                            PropName = dp.PropName,
                            PropValue = dp.PropValue,
                        })),
                    // Modbus属性集合
                    VariableList = new ObservableCollection<VariableModel>(
                        d.Vars.Select(dv => new VariableModel
                        {
                            VarNum = dv.VarNum,
                            VarName = dv.Header,
                            VarAddress = dv.Address,
                            Offset = dv.Offset,
                            Modulus = dv.Modulus,
                            VarType = dv.VarType,
                        }))
                };
            });
            DeviceList = ds.ToList();

            this.RaisePropertyChanged(nameof(DeviceList));
        }

        /// <summary>
        /// 内容页面切换
        /// </summary>
        /// <param name="obj"></param>
        private void ShowPage(object obj)
        {
            // Bug：对象会重复创建，需要处理
            // 第80行解决

            var model = obj as MenuModel; // 获取选中菜单
            if (model != null)
            {
                // 判断如果是当前页则返回
                if (ViewContent != null && ViewContent.GetType().Name == model.TargetView) return;

                // 通过反射实例化要显示的页面
                Type type = Assembly.Load("Zhy.DigitaPlatform.Views")
                    .GetType("Zhy.DigitaPlatform.Views.Pages." + model.TargetView)!;
                ViewContent = Activator.CreateInstance(type)!;
            }
        }

        /// <summary>
        /// 组态编辑按钮方法
        /// </summary>
        private void ShowConfig()
        {
            if (GlobalUserInfo.UserType > 0) // 根据用户判断权限
            {
                this.ViewBlur = 5; // 设置弹窗时主窗口显示蒙板效果
                // 可以打开编辑   启动窗口   主动
                if (ActionManager.ExecuteAndResult<object>("AAA", null))
                {
                    // 添加一个等待页面（预留）

                    // 可能会有耗时控件
                    cts.Cancel(); // 取消组件的通讯请求
                    // 等待所有组件的通讯请求结束
                    Task.WaitAll(tasks.ToArray());
                    // 重置接收数据线程启用状态
                    cts = new CancellationTokenSource();
                    tasks.Clear();

                    // 刷新   配置文件/数据库
                    ComponentsInit(); // 重新初始化组件
                    // 启动监听
                    this.Monitor();
                }
                this.ViewBlur = 0;
            }
            else
            {
                // 提示没有权限操作
            }
        }

        /// <summary>
        /// 清空
        /// </summary>
        public override void Cleanup()
        {
            base.Cleanup();
        }
    }
}
